#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "ThreadPool.hpp"
#include "InetAddr.hpp"
#include "LockGuard.hpp"
#include <pthread.h>
#include <functional>

using task_t = std::function<void()>;

class Route
{
private:
    void Check(InetAddr &who)
    {
        for (auto &user : _online_user)
        {
            if (user == who)
                return;
        }
        _online_user.push_back(who);
    }

public:
    Route()
    {
        pthread_mutex_init(&_mutex, nullptr);
    }

    void Offline(InetAddr who)
    {
        std::vector<InetAddr>::iterator it = _online_user.begin();
        for (; it != _online_user.end(); it++)
        {
            if (*it == who)
            {
                _online_user.erase(it);
                break;
            }
        }
    }

    void ForwardHelper(int sockfd, const std::string message, InetAddr who)
    {
        LockGuard lock(&_mutex);
        Check(who);
        if (message == "Q" || message == "QUIT")
            Offline(who);
        std::string Send_message("[" + who.AddrStr() + "]" + message);
        for (auto &user : _online_user)
        {
            struct sockaddr_in peer = user.Addr();
            sendto(sockfd, Send_message.c_str(), Send_message.size(), 0, (struct sockaddr *)&peer, sizeof(peer));
        }
    }

    void Forward(int sockfd, const std::string &message, InetAddr &who)
    {
        task_t task = std::bind(&Route::ForwardHelper, this, sockfd, message, who);
        ThreadPool<task_t>::GetInstance()->Equeue(task);
    }

    ~Route()
    {
        pthread_mutex_destroy(&_mutex);
    }

private:
    std::vector<InetAddr> _online_user;
    pthread_mutex_t _mutex;
};